using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;

namespace  GoodStuff.Web.Controls
{
	/// <summary>
	/// A templated databound repeater that dynamically determines which
	/// template to use based on the class of the object in the collection.
	/// </summary>
	[ParseChildren(false)]
	[ControlBuilder(typeof(ObjectRepeaterControlBuilder))]
	public class ObjectRepeater : System.Web.UI.Control, System.Web.UI.INamingContainer, IParserAccessor
	{
		private Hashtable _templates = new Hashtable();
		private string _defaultTemplateName = null;
		private IEnumerable _dataSource;

		public ObjectRepeaterDetermineTemplateDelegate DetermineTemplate;
		public event ObjectRepeaterItemEventHandler ItemCreated;
		public event ObjectRepeaterItemEventHandler ItemDataBound;
		public event RepeaterCommandEventHandler ItemCommand;

		#region Properties

		public string DefaultTemplate
		{
			get
			{
				return _defaultTemplateName;
			}
			set
			{
				_defaultTemplateName = value;
			}
		}
		
		public virtual IEnumerable DataSource
		{
			get
			{
				return _dataSource;
			}
			set
			{
				_dataSource = value;
			}
		}

		public ControlCollection Items
		{
			get
			{
				return this.Controls;
			}
		}

		#endregion

		#region Methods

		public void OnItemDataBound(ObjectRepeaterItemEventArgs e)
		{
			if(this.ItemDataBound != null)
				this.ItemDataBound(this, e);
		}

		public void OnItemCreated(ObjectRepeaterItemEventArgs e)
		{
			if(this.ItemCreated != null)
				this.ItemCreated(this, e);
		}

		public void OnItemCommand(RepeaterCommandEventArgs e)
		{
			if(this.ItemCommand != null)
				this.ItemCommand(this, e);
		}

		public override void DataBind() 
		{
			// Controls with a data-source property perform their 
			// custom data binding by overriding DataBind to
			// evaluate any data-binding expressions on the control    
			// itself.
			base.OnDataBinding(EventArgs.Empty);

			// Reset the control's state.
			Controls.Clear();
			ClearChildViewState();

			// Create the control hierarchy using the data source.
			CreateControlHierarchy(true);
			ChildControlsCreated = true;

			TrackViewState();
		
		}

		protected override void CreateChildControls() 
		{
			Controls.Clear();

			if (ViewState["ItemCount"] != null) 
			{
				// Create the control hierarchy using the view state, 
				// not the data source.
				CreateControlHierarchy(false);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="useDataSource">True to create the hierarchy from the DataSource, False to create it from the ViewState.</param>
		private void CreateControlHierarchy(bool useDataSource) 
		{
			IEnumerable dataSource = null;
			int count = -1;

			if (useDataSource == false) 
			{
				// ViewState must have a non-null value for ItemCount because this is checked 
				//  by CreateChildControls.
				count = (int)ViewState["ItemCount"];
				if (count != -1) 
				{
					dataSource = new DummyDataSource(count);
				}
			}
			else 
			{
				dataSource = this._dataSource;
			}

			if (dataSource != null) 
			{
				int index = 0;
				count = 0;
				foreach (object dataItem in dataSource) 
				{
					CreateItem(index, ListItemType.Item, useDataSource, dataItem);

					count++;
					index++;
				}
			}

			if (useDataSource) 
			{
				// Save the number of items contained for use in round trips.
				ViewState["ItemCount"] = ((dataSource != null) ? count : -1);
			}
		}

//		private ArrayList TemplateNames
//		{
//		}

		private RepeaterItem CreateItem(int itemIndex, ListItemType itemType, bool dataBind, object dataItem) 
		{
			RepeaterItem item = new RepeaterItem(itemIndex, itemType);
			
			//decide which template to use.
			string templateName = null;
			
			if(dataBind)
			{
				if(DetermineTemplate != null)
				{
					templateName = this.DetermineTemplate(this, dataItem);
					ViewState["templateName" + itemIndex.ToString()] = templateName;
				}
			}
			else
			{
				//determine template to use from viewState;
				templateName = (string)ViewState["templateName" + itemIndex.ToString()];
			}

			if(templateName == null)
				templateName = this.DefaultTemplate;
			CustomDynamicTemplate dynamicTemplate = (CustomDynamicTemplate)_templates[templateName];
			
			ObjectRepeaterItemEventArgs e = new ObjectRepeaterItemEventArgs(item, templateName );

			//Must exist.
			dynamicTemplate.ItemTemplate.InstantiateIn(item);
			
			if (dataBind) 
			{
				item.DataItem = dataItem;
			}
			OnItemCreated(e);
			this.Controls.Add(item);

			if (dataBind) 
			{
				item.DataBind();
				OnItemDataBound(e);

				item.DataItem = null;
			}

			return item;
		}

		protected override bool OnBubbleEvent(object source, EventArgs e) 
		{
			// Handle events raised by children by overriding OnBubbleEvent.

			bool handled = false;

			if (e is CommandEventArgs) 
			{
				RepeaterCommandEventArgs ce = (RepeaterCommandEventArgs)e;

				OnItemCommand(ce);
				handled = true;
			}

			return handled;
		}


		/// <summary>
		/// This member overrides Control.AddParsedSubObject.
		/// it catches the contents of each item.
		/// </summary>
		protected override void AddParsedSubObject(object obj)
		{
			if(obj is CustomDynamicTemplate)
			{	
				CustomDynamicTemplate template = (CustomDynamicTemplate)obj;
				_templates.Add(template.Name, template);
				return;
			}
			else
			{
				if(!(obj is LiteralControl))
					throw new Exception("Tabstrip can only have children of type 'tabstrippanel'");
			}
			base.AddParsedSubObject(obj);
		}	

		#endregion
	}	

	public delegate string ObjectRepeaterDetermineTemplateDelegate(object sender, object dataItem);

	public delegate void ObjectRepeaterItemEventHandler(object sender, ObjectRepeaterItemEventArgs e);

	public class ObjectRepeaterItemEventArgs : EventArgs
	{
		private string _templateName;
		private RepeaterItem _item;

		public ObjectRepeaterItemEventArgs(RepeaterItem item, string templateName)
		{
			_item = item;
			_templateName = templateName;
		}

		public RepeaterItem Item
		{
			get 
			{
				return _item; 
			}
		}

		public string TemplateName
		{
			get { return _templateName; }
		}
	}

	#region BasicDataSource class
	internal sealed class DummyDataSource : ICollection 
	{
		private int dataItemCount;
		public DummyDataSource(int dataItemCount) 
		{
			this.dataItemCount = dataItemCount;
		}
		public int Count 
		{
			get 
			{
				return dataItemCount;
			}
		}
		public bool IsReadOnly 
		{
			get 
			{
				return false;
			}
		}
		public bool IsSynchronized 
		{
			get 
			{
				return false;
			}
		}
		public object SyncRoot 
		{
			get 
			{
				return this;
			}
		}
		public void CopyTo(Array array, int index) 
		{
			for (IEnumerator e = this.GetEnumerator(); e.MoveNext();)
				array.SetValue(e.Current, index++);
		}
		public IEnumerator GetEnumerator() 
		{
			return new BasicDataSourceEnumerator(dataItemCount);
		}

		private class BasicDataSourceEnumerator : IEnumerator 
		{
			private int count;
			private int index;
			public BasicDataSourceEnumerator(int count) 
			{
				this.count = count;
				this.index = -1;
			}
			public object Current 
			{
				get 
				{
					return null;
				}
			}
			public bool MoveNext() 
			{
				index++;
				return index < count;
			}
			public void Reset() 
			{
				this.index = -1;
			}
		}
	}
	#endregion

	#region Control Builder
	/// <summary>
	/// Interacts with the parser to build a PanelBar control.
	/// </summary>
	public class ObjectRepeaterControlBuilder : ControlBuilder
	{
		/// <summary>
		/// This member overrides ControlBuilder.GetChildControlType.
		/// </summary>
		public override Type GetChildControlType(string tagName, IDictionary attributes)
		{
			// check is the tag is an TabStripPanelItem tag
			if (tagName.ToLower().IndexOf("objecttemplate") >= 0)
			{
				// yes, so return TabStripPanelItem type
				return typeof(CustomDynamicTemplate);
			}
			return null;
		}

		public override void AppendLiteralString(string s)
		{
			//empty implementation to drop literal content.
			System.Diagnostics.Trace.WriteLine(s);
		}

	}
	#endregion

	[ParseChildren(true)]
	public class CustomDynamicTemplate : Control
	{
		private string _name;
		private ITemplate _itemTemplate;

		[TemplateContainer(typeof(RepeaterItem))]
		public ITemplate ItemTemplate
		{
			get { return _itemTemplate; }
			set { _itemTemplate = value; }
		}

		public string Name
		{
			get { return _name; }
			set { _name = value; }
		}
	}
}
